package cn.hxth.dubbo.customer.shiro.config;


import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import cn.hxth.dubbo.customer.shiro.cache.manager.RedisCacheManager;
import cn.hxth.dubbo.customer.shiro.factory.StatelessDefaultSubjectFactory;
import cn.hxth.dubbo.customer.shiro.filter.KickoutSessionControlFilter;
import cn.hxth.dubbo.customer.shiro.realm.MemberRealm;
import cn.hxth.dubbo.customer.shiro.realm.matcher.DefaultCredentialsMatcher;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.session.mgt.eis.SessionIdGenerator;
import org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSubjectFactory;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.filter.DelegatingFilterProxy;
import redis.clients.jedis.JedisPoolConfig;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.Map;

;

// @Configuration
@PropertySource({"classpath:shiro.properties"})
public class ShiroConfig {
    public RedisConnectionFactory getRedisConnectionFactory(String hostName, int port,
                                                            int timeout, int maxTotal, int maxIdle,
                                                            long maxWaitMillis, int database, boolean testOnBorrow) { // 是负责建立Factory的连接工厂类
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setTestOnBorrow(testOnBorrow);
        jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
        jedisPoolConfig.setMaxTotal(maxTotal);
        jedisPoolConfig.setMaxIdle(maxIdle);
        JedisConnectionFactory jedisFactory = new JedisConnectionFactory();
        jedisFactory.setHostName(hostName);
        jedisFactory.setPort(port);
        jedisFactory.setDatabase(database);
        jedisFactory.setTimeout(timeout);
        //jedisPoolConfig.setMinIdle(this.minIdle);
        jedisFactory.setPoolConfig(jedisPoolConfig);
        jedisFactory.afterPropertiesSet(); // 初始化连接池配置
        return jedisFactory;
    }
   /* @Value("${redis.host}")
    String hostName;
    @Value("${redis.port}")
    int port;
    @Value("${redis.timeout}")
    int timeout;
    @Value("${redis.pool.maxWaitMillis}")
    private long maxWaitMillis;
    @Value("${redis.pool.maxIdle}")
    private int maxIdle;
    @Value("${redis.pool.testOnBorrow}")
    private boolean testOnBorrow;
    @Value("${redis.pool.maxTotal}")
    private int maxTotal;
    @Value("${redis.db.kickout}")
    private int kickoutDatabase;
    @Value("${redis.db.retryCount}")
    private int retryCountDatabase;
    @Value("${redis.db.activeSessionCache}")
    private int activeSessionCacheDatabase;
    @Value("${redis.db.authorization}")
    private int authorizationDatabase;
    @Value("${redis.db.authentication}")
    private int authenticationDatabase;
    @Value("${shiro.retry.expire}")
    private int expire ;
    @Value("${shiro.retry.max}")
    private  int retryMax ;*/
   @Bean
   public  JedisPoolConfig getJedisPoolConfig(@Value("${redis.pool.testOnBorrow}") boolean testOnBorrow, @Value("${redis.pool.maxWaitMillis}") long maxWaitMillis, @Value("${redis.pool.maxTotal}") int maxTotal, @Value("${redis.pool.maxIdle}") int maxIdle){
       JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
       jedisPoolConfig.setTestOnBorrow(testOnBorrow);
       jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
       jedisPoolConfig.setMaxTotal(maxTotal);
       jedisPoolConfig.setMaxIdle(maxIdle);
       return  jedisPoolConfig ;
   }
   @Bean("redisTemplateAuthentication")
    public RedisTemplate<String,Object> getRedisTemplateAuthentication(
           @Value("${redis.host}")
            String hostName,
           @Value("${redis.db.authentication}")
            int database,
           @Value("${redis.port}")
            int port,
           @Value("${redis.timeout}")
            int timeout, @Value("${redis.pool.testOnBorrow}") boolean testOnBorrow, @Value("${redis.pool.maxWaitMillis}") long maxWaitMillis, @Value("${redis.pool.maxTotal}") int maxTotal, @Value("${redis.pool.maxIdle}") int maxIdle) { // 是负责建立Factory的连接工厂类
        RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>() ;
        redisTemplate.setConnectionFactory(this.getRedisConnectionFactory(hostName,port,timeout,maxTotal,maxIdle,maxWaitMillis,database,testOnBorrow));
        return redisTemplate;
    }
    @Bean("redisTemplateAuthorization")
    public RedisTemplate<String,Object> getRedisTemplateAuthorization(
            @Value("${redis.host}")
                    String hostName,
            @Value("${redis.db.authorization}")
                    int database,
            @Value("${redis.port}")
                    int port,
            @Value("${redis.timeout}")
                    int timeout, @Value("${redis.pool.testOnBorrow}") boolean testOnBorrow, @Value("${redis.pool.maxWaitMillis}") long maxWaitMillis, @Value("${redis.pool.maxTotal}") int maxTotal, @Value("${redis.pool.maxIdle}") int maxIdle) { // 是负责建立Factory的连接工厂类
        RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>() ;
        redisTemplate.setConnectionFactory(this.getRedisConnectionFactory(hostName,port,timeout,maxTotal,maxIdle,maxWaitMillis,database,testOnBorrow));
        return redisTemplate;
    }
    @Bean("redisTemplateActiveSessionCache")
    public RedisTemplate<String,Object> getRedisTemplateActiveSessionCache(
            @Value("${redis.host}")
                    String hostName,
            @Value("${redis.db.activeSessionCache}")
                    int database,
            @Value("${redis.port}")
                    int port,
            @Value("${redis.timeout}")
                    int timeout, @Value("${redis.pool.testOnBorrow}") boolean testOnBorrow, @Value("${redis.pool.maxWaitMillis}") long maxWaitMillis, @Value("${redis.pool.maxTotal}") int maxTotal, @Value("${redis.pool.maxIdle}") int maxIdle) { // 是负责建立Factory的连接工厂类
        RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>() ;
        redisTemplate.setConnectionFactory(this.getRedisConnectionFactory(hostName,port,timeout,maxTotal,maxIdle,maxWaitMillis,database,testOnBorrow));
        return redisTemplate;
    }
    @Bean("redisTemplateKickout")
    public RedisTemplate<String,Object> getRedisTemplateKickout(
            @Value("${redis.host}")
                    String hostName,
            @Value("${redis.db.kickout}")
                    int database,
            @Value("${redis.port}")
                    int port,
            @Value("${redis.timeout}")
                    int timeout, @Value("${redis.pool.testOnBorrow}") boolean testOnBorrow, @Value("${redis.pool.maxWaitMillis}") long maxWaitMillis, @Value("${redis.pool.maxTotal}") int maxTotal, @Value("${redis.pool.maxIdle}") int maxIdle) { // 是负责建立Factory的连接工厂类
        RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>() ;
        redisTemplate.setConnectionFactory(this.getRedisConnectionFactory(hostName,port,timeout,maxTotal,maxIdle,maxWaitMillis,database,testOnBorrow));
        return redisTemplate;
    }
    @Bean("redisTemplateRetryCount")
    public RedisTemplate<String,Object> getRedisTemplateRetryCount(
            @Value("${redis.host}")
                    String hostName,
            @Value("${redis.db.retryCount}")
                    int database,
            @Value("${redis.port}")
                    int port,
            @Value("${redis.timeout}")
                    int timeout, @Value("${redis.pool.testOnBorrow}") boolean testOnBorrow, @Value("${redis.pool.maxWaitMillis}") long maxWaitMillis, @Value("${redis.pool.maxTotal}") int maxTotal, @Value("${redis.pool.maxIdle}") int maxIdle) { // 是负责建立Factory的连接工厂类
        RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>() ;
        redisTemplate.setConnectionFactory(this.getRedisConnectionFactory(hostName,port,timeout,maxTotal,maxIdle,maxWaitMillis,database,testOnBorrow));
        return redisTemplate;
    }

    @Bean
    public ShiroDialect getShiroDialect() {	// 必须配置此操作才可以使用thymeleaf-extras-shiro开发包
        return new ShiroDialect() ;
    }
@Bean

    public RedisCacheManager getRedisCacheManager(Map<String,RedisTemplate<String,Object>> map) {


        RedisCacheManager redisCacheManager = new RedisCacheManager();
        Map<String, RedisTemplate<String,Object>> factoryMap = new HashMap<>();
        factoryMap.put("authenticationCache", map.get("redisTemplateAuthentication"));
        factoryMap.put("authorizationCache", map.get("redisTemplateAuthorization"));
        factoryMap.put("activeSessionCache", map.get("redisTemplateActiveSessionCache"));
        factoryMap.put("retryCount", map.get("redisTemplateRetryCount"));
        factoryMap.put("kickout", map.get("redisTemplateKickout"));
        redisCacheManager.setConnectionFactoryMap(factoryMap);
        return redisCacheManager;
    }

    @Bean
    public RememberMeManager getRememberManager() { // 5
        CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
        SimpleCookie cookie = new SimpleCookie("hxth-rememberme");
        cookie.setHttpOnly(true);
        cookie.setMaxAge(2592000);
        rememberMeManager.setCookie(cookie);
        return rememberMeManager;
    }
    @Bean
    public MemberRealm getMemberRealm(RedisCacheManager cacheManager,
                                      @Value("${shiro.retry.expire}")
                                       int expire ,
                                      @Value("${shiro.retry.max}")int retryMax){
        MemberRealm memberRealm = new MemberRealm() ;
        memberRealm.setCachingEnabled(true);
        memberRealm.setAuthenticationCachingEnabled(true);
        memberRealm.setAuthenticationCacheName("authenticationCache");
        memberRealm.setAuthorizationCachingEnabled(true);
        memberRealm.setAuthorizationCacheName("authorizationCache") ;
        DefaultCredentialsMatcher defaultCredentialsMatcher = new DefaultCredentialsMatcher() ;
        defaultCredentialsMatcher.setCacheManager(cacheManager);
        defaultCredentialsMatcher.setExpire(String.valueOf(expire));
        defaultCredentialsMatcher.setMaxRetryCount(retryMax);
        memberRealm.setCredentialsMatcher(defaultCredentialsMatcher);

        return  memberRealm ;
    }
    @Bean
    public QuartzSessionValidationScheduler getQuartzSessionValidationScheduler() {
        QuartzSessionValidationScheduler sessionValidationScheduler = new QuartzSessionValidationScheduler();
        sessionValidationScheduler.setSessionValidationInterval(100000);
        return sessionValidationScheduler;
    }
    @Bean
    public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(
            DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
        aasa.setSecurityManager(securityManager);
        return aasa;
    }


    @Bean
    public DefaultWebSessionManager getSessionManager(SessionDAO sessionDAO,
                                                      QuartzSessionValidationScheduler sessionValidationScheduler) { // 6
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setGlobalSessionTimeout(1000000);
        sessionManager.setDeleteInvalidSessions(true);
        sessionManager.setSessionValidationScheduler(sessionValidationScheduler);
        sessionManager.setSessionValidationSchedulerEnabled(true);
        sessionManager.setSessionDAO(sessionDAO);
        SimpleCookie sessionIdCookie = new SimpleCookie("hxth-session-id");
        sessionManager.setSessionIdCookie(sessionIdCookie);
        sessionManager.setSessionIdCookieEnabled(true);
        sessionManager.setSessionValidationSchedulerEnabled(false);
        return sessionManager;
    }
    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
        daap.setProxyTargetClass(true);
        return daap;
    }
    @Bean
    public DefaultWebSecurityManager getSecurityManager(Realm memberRealm, RedisCacheManager cacheManager,
                                                        SessionManager sessionManager) {
        RememberMeManager rememberMeManager = this.getRememberManager() ;// 7
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(memberRealm);
        securityManager.setCacheManager(cacheManager);
        securityManager.setSessionManager(sessionManager);
        securityManager.setRememberMeManager(rememberMeManager);
       securityManager.setSubjectFactory(subjectFactory());
        return securityManager;
    }

    @Bean
    public SessionIdGenerator getSessionIdGenerator() { // 3
        return new JavaUuidSessionIdGenerator();
    }

    @Bean
    public SessionDAO getSessionDAO(SessionIdGenerator sessionIdGenerator) {
        EnterpriseCacheSessionDAO sessionDAO = new EnterpriseCacheSessionDAO() ;
        sessionDAO.setActiveSessionsCacheName("activeSessionCache");
        sessionDAO.setSessionIdGenerator(sessionIdGenerator);
        return sessionDAO;
    }
   /* public DefaultFormAuthenticationFilter getDefaultFormAuthenticationFilter(){
        DefaultFormAuthenticationFilter defaultFormAuthenticationFilter = new DefaultFormAuthenticationFilter() ;
        defaultFormAuthenticationFilter.setUsernameParam("mid");
        defaultFormAuthenticationFilter.setPasswordParam("password");
        defaultFormAuthenticationFilter.setRememberMeParam("rememberme");
        defaultFormAuthenticationFilter.setLoginUrl("/loginForm");
        defaultFormAuthenticationFilter.setFailureKeyAttribute("error");
        defaultFormAuthenticationFilter.setRandname("randname");
        defaultFormAuthenticationFilter.setRandparam("code");
        return  defaultFormAuthenticationFilter ;
    }*/
   @Bean
   public FormAuthenticationFilter getFormAuthenticationFilte(){
       FormAuthenticationFilter formAuthenticationFilter = new FormAuthenticationFilter() ;
       formAuthenticationFilter.setUsernameParam("mid");
       formAuthenticationFilter.setPasswordParam("password");
       formAuthenticationFilter.setRememberMeParam("rememberme");
       formAuthenticationFilter.setLoginUrl("/loginForm");
       formAuthenticationFilter.setFailureKeyAttribute("error");
       return  formAuthenticationFilter ;
   }

    public LogoutFilter getLogoutFilter() { // 在ShiroFilterFactoryBean中使用
        LogoutFilter logoutFilter = new LogoutFilter();
        logoutFilter.setRedirectUrl("/logout");	// 首页路径，登录注销后回到的页面
        return logoutFilter;
    }
    @Bean
    public KickoutSessionControlFilter getKickoutSessionControlFilter(RedisCacheManager redisCacheManager, DefaultWebSessionManager  sessionManager){
        KickoutSessionControlFilter kickoutSessionControlFilter = new KickoutSessionControlFilter() ;
        kickoutSessionControlFilter.setKickoutAfter(false);
        kickoutSessionControlFilter.setCacheManager(redisCacheManager);
        kickoutSessionControlFilter.setMaxSessionCount(10);
        kickoutSessionControlFilter.setKickoutUrl("/loginForm");
        kickoutSessionControlFilter.setSessionManager(sessionManager);
        return  kickoutSessionControlFilter ;
    }
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager,KickoutSessionControlFilter kickoutSessionControlFilter) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        shiroFilterFactoryBean.setLoginUrl("/loginForm");	// 设置登录页路径
        shiroFilterFactoryBean.setSuccessUrl("/pages/back/welcome");	// 设置跳转成功页
        shiroFilterFactoryBean.setUnauthorizedUrl("/unauth");	// 授权错误页
        Map<String, Filter> filters = new HashMap<>();
        filters.put("authc", this.getFormAuthenticationFilte());
        filters.put("logout", this.getLogoutFilter());
        filters.put("kickout",kickoutSessionControlFilter) ;
        shiroFilterFactoryBean.setFilters(filters);
        Map<String, String> filterChainDefinitionMap = new HashMap<String, String>();
        filterChainDefinitionMap.put("/logout.page", "logout");
        filterChainDefinitionMap.put("/loginForm", "authc");	// 定义内置登录处理
        filterChainDefinitionMap.put("/pages/**", "authc,kickout");
        filterChainDefinitionMap.put("/*", "anon");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

   @Bean
    public FilterRegistrationBean delegatingFilterProxy(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        DelegatingFilterProxy proxy = new DelegatingFilterProxy();
        proxy.setTargetFilterLifecycle(true);
        proxy.setTargetBeanName("shiroFilter");
        filterRegistrationBean.setFilter(proxy);
        return filterRegistrationBean;
    }
    @Bean
    public DefaultWebSubjectFactory subjectFactory(){
        StatelessDefaultSubjectFactory statelessDefaultSubjectFactory = new StatelessDefaultSubjectFactory();
        return statelessDefaultSubjectFactory;
    }


}
